﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Resources;
using System.Data.SqlClient;
using System.Xml;
using System.IO;

using XEvents.Library;
using XEvents.Library.Targets;
using WeifenLuo.WinFormsUI.Docking;

namespace ExtendedEventsManager
{
    public partial class ServerExplorerForm : ToolWindow
    {
        private List<SqlServer> servers = new List<SqlServer>();
        internal Properties.Settings s = Properties.Settings.Default;

        
        public void AddServer(SqlServer server)
        {
            this.servers.Add(server);
            BindTreeView(server);
        }


        public ServerExplorerForm()
        {
            InitializeComponent();
        }

        private void BindTreeView(SqlServer server)
        {
            XmlDocument xdoc = server.GetSessionXml();
            System.Diagnostics.Debug.WriteLine(xdoc.InnerXml);
            TreeNode tNode = new TreeNode();

            if (!treeView1.Nodes.ContainsKey(server.ServerName))
            {
                treeView1.Nodes.Add(new XEventTreeNode(server.ServerName, xdoc.DocumentElement));
            }

            tNode = treeView1.Nodes.Find(server.ServerName, false).ElementAt(0);

            tNode.Nodes.Clear();

            // SECTION 3. Populate the TreeView with the DOM nodes.
            AddNode(xdoc.DocumentElement, tNode);
            //treeView1.ExpandAll();
        }
        private void AddNode(XmlNode inXmlNode, TreeNode inTreeNode)
        {
            XmlNode xNode;
            TreeNode tNode;
            XmlNodeList nodeList;
            int i;

            // Loop through the XML nodes until the leaf is reached.
            // Add the nodes to the TreeView during the looping process.
            if (inXmlNode.HasChildNodes)
            {
                nodeList = inXmlNode.ChildNodes;
                for (i = 0; i <= nodeList.Count - 1; i++)
                {
                    xNode = inXmlNode.ChildNodes[i];
                    if (xNode.Attributes == null)
                        inTreeNode.Nodes.Add(new XEventTreeNode(xNode.Name, xNode));
                    else if (xNode.Attributes["Name"] == null)
                        inTreeNode.Nodes.Add(new XEventTreeNode(xNode.Name, xNode));
                    else
                    {
                        XEventTreeNode xetn = new XEventTreeNode(xNode.Attributes["Name"].Value, xNode);
                        inTreeNode.Nodes.Add(xetn);
                    }
                    tNode = inTreeNode.Nodes[i];
                    AddNode(xNode, tNode);
                }
            }
            else
            {

                // Here you need to pull the data from the XmlNode based on the
                // type of node, whether attribute values are required, and so forth.

                if (inXmlNode.Attributes == null || inXmlNode.Attributes["Name"] == null)
                    inTreeNode.Text = inXmlNode.Name;
                else
                {
                    inTreeNode.Text = inXmlNode.Attributes["Name"].Value;
                }
            }

        }

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            TreeNode tNode = e.Node;
            XmlNode xnode = (XmlNode)tNode.Tag;

            string nodeInfo = string.Empty;

            if (xnode != null)
            {
                if (xnode.ParentNode.Name == "Value")
                {
                    nodeInfo = xnode.Value;
                }
                else
                {
                    foreach (XmlAttribute xa in xnode.Attributes)
                    {
                        nodeInfo += xa.Name + ": " + xa.Value + "\r\n";
                    }
                }
                ((MainForm)this.ParentForm).Details.Information = nodeInfo;

                
                ((MainForm)this.ParentForm).PropertiesForm.PropertyGrid.SelectedObject = this.SelectedSession;
            }
        }

        private void BindSelectedNodeProperties()
        {
            TreeNode tnode = treeView1.SelectedNode;

        }
        private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            treeView1.SelectedNode = e.Node;
            XmlNode xNode = (XmlNode)e.Node.Tag;
            if (e.Button == MouseButtons.Right)
            {
                if (e.Node.Level == 0)
                {
                    GenerateServerMenu();
                    contextMenuStrip1.Show(treeView1, e.Location);
                }
                else if (xNode.Name == "EventSession")
                {
                    GenerateSessionMenu(xNode);
                    contextMenuStrip1.Show(treeView1, e.Location);
                }
                else if (xNode.Name == "EventSessionTarget")
                {
                    contextMenuStrip1.Items.Clear();
                    ToolStripMenuItem tsmi;

                    tsmi = new ToolStripMenuItem("View Target Data");
                    tsmi.Click += new EventHandler(ViewTargetData_Click);
                    tsmi.Enabled = (xNode.ParentNode.ParentNode.Attributes["SessionState"].Value == "Start");

                    contextMenuStrip1.Items.Add(tsmi);
                    contextMenuStrip1.Show(treeView1, e.Location);
                }
            }
        }

        private void GenerateServerMenu()
        {
            contextMenuStrip1.Items.Clear();
            ToolStripMenuItem tsmi = new ToolStripMenuItem("New Event Session");
            tsmi.Click += new EventHandler(NewEventSession_Click);
            contextMenuStrip1.Items.Add(tsmi);

            contextMenuStrip1.Items.Add(new ToolStripSeparator());

            tsmi = new ToolStripMenuItem("Refresh");
            tsmi.Click += new EventHandler(Refresh_Click);
            contextMenuStrip1.Items.Add(tsmi);

            contextMenuStrip1.Items.Add(new ToolStripSeparator());

            tsmi = new ToolStripMenuItem("View XEvent Metadata");
            tsmi.Click +=new EventHandler(ViewMetadata_Click);
            contextMenuStrip1.Items.Add(tsmi);
        }

        private void GenerateSessionMenu(XmlNode xNode)
        {
            contextMenuStrip1.Items.Clear();
            ToolStripMenuItem tsmi;
            ToolStripMenuItem subtsmi;

            tsmi = new ToolStripMenuItem("Edit Event Session");
            tsmi.Click += new EventHandler(EditEventSession_Click);
            contextMenuStrip1.Items.Add(tsmi);

            contextMenuStrip1.Items.Add(new ToolStripSeparator());
            
            tsmi = new ToolStripMenuItem("Drop Event Session");
            tsmi.Click += new EventHandler(DropEventSession_Click);
            contextMenuStrip1.Items.Add(tsmi);

            contextMenuStrip1.Items.Add(new ToolStripSeparator());

            tsmi = new ToolStripMenuItem("Script Session as");
            
            subtsmi = new ToolStripMenuItem("CREATE to");
            subtsmi.DropDownItems.Add(new ToolStripMenuItem("File", null, new EventHandler(ScriptToFile_Click)));
            subtsmi.DropDownItems.Add(new ToolStripMenuItem("Clipboard", null, new EventHandler(ScriptToClipboard_Click)));
            subtsmi.DropDownItems.Add(new ToolStripMenuItem("New SSMS Window", null, new EventHandler(ScriptToSSMS_Click)));
            tsmi.DropDownItems.Add(subtsmi);

            subtsmi = new ToolStripMenuItem("DROP to");
            subtsmi.DropDownItems.Add(new ToolStripMenuItem("File", null, new EventHandler(ScriptToFile_Click)));
            subtsmi.DropDownItems.Add(new ToolStripMenuItem("Clipboard", null, new EventHandler(ScriptToClipboard_Click)));
            subtsmi.DropDownItems.Add(new ToolStripMenuItem("New SSMS Window", null, new EventHandler(ScriptToSSMS_Click)));
            tsmi.DropDownItems.Add(subtsmi);


            contextMenuStrip1.Items.Add(tsmi);
            contextMenuStrip1.Items.Add(new ToolStripSeparator());
            
            tsmi = new ToolStripMenuItem("Start Event Session");
            tsmi.Click += new EventHandler(StartEventSession_Click);
            if (xNode.Attributes["SessionState"] != null)
            {
                tsmi.Enabled = !(xNode.Attributes["SessionState"].Value == "Start");
            }
            contextMenuStrip1.Items.Add(tsmi);
            
            tsmi = new ToolStripMenuItem("Stop Event Session");
            tsmi.Click += new EventHandler(StopEventSession_Click);
            if (xNode.Attributes["SessionState"] != null)
            {
                tsmi.Enabled = (xNode.Attributes["SessionState"].Value == "Start");
            }
            contextMenuStrip1.Items.Add(tsmi);

        }

        #region Session Events

        private void ChangeSessionState(EventSession session, SessionState State)
        {
            try
            {
                session.ChangeSessionState(State);

                // TODO: Fix this to remove current node 
                ReloadNode(session.SqlServer.ServerName, session.Name);
                //Load_Form();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "An Error Occured!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void DropEventSession(EventSession ses)
        {
            try
            {
                ses.DropEventSession();

                // TODO: Fix this to remove current node 
                RemoveNode(ses.SqlServer.ServerName, ses.Name);
                //Load_Form();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "An Error Occured!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion

        #region Menu Click Events

        void ViewTargetData_Click(object sender, EventArgs e)
        {
            TargetDataViewer tt = new TargetDataViewer(this.SelectedTarget.TargetData);
            tt.TabText = "[" + this.CurrentServer.ServerName + "] " + this.SelectedTarget.Name;
            tt.Text = tt.TabText;
            tt.ToolTipText = tt.TabText;
            tt.MdiParent = this.MdiParent;
            tt.Show(this.DockPanel, DockState.Document);
            tt.SetEventHandlers();
        }
        void StartEventSession_Click(object sender, EventArgs e)
        {
            DialogResult dr = MessageBox.Show("Are you sure you want to start collection for the " + this.SelectedSession.Name + " session?", "Start Session Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (dr == DialogResult.Yes)
            {
                ChangeSessionState(this.SelectedSession, SessionState.Start);
            }
        }
        void StopEventSession_Click(object sender, EventArgs e)
        {
            
            DialogResult dr = MessageBox.Show("Are you sure you want to stop collection for the " + this.SelectedSession.Name + " session?", "Stop Session Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (dr == DialogResult.Yes)
            {
                bool tdv_found = false;
                foreach (IDockContent idoc in this.DockPanel.Documents)
                {
                    if (idoc is TargetDataViewer)
                    {
                        TargetDataViewer tdv = (TargetDataViewer)idoc;

                        foreach (TreeNode tn in treeView1.SelectedNode.Nodes["Targets"].Nodes)
                        {
                            if (tdv.TabText == "[" + this.CurrentServer.ServerName + "] " + tn.Name)
                            {
                                tdv.BringToFront();
                                tdv.SessionStopped();
                                tdv_found = true;
                            }
                        }
                    }
                }
                if (tdv_found)
                    MessageBox.Show("The Session has been stopped.  Updates are no longer available.\r\n\r\nTo resume updating data, close the Target Viewer, restart the Event Session and then reopen the Target Viewer.", "Event Session Stop Notification!", MessageBoxButtons.OK, MessageBoxIcon.Information);

                ChangeSessionState(this.SelectedSession, SessionState.Stop);
            }
        }

        void ScriptToFile_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem tsmi = (ToolStripMenuItem)sender;
            string script = string.Empty;
            switch (tsmi.OwnerItem.Text)
            {
                case "CREATE to":
                    script = this.SelectedSession.GenerateSQLScript();
                    break;
                case "DROP to":
                    script = this.SelectedSession.GenerateDropScript();
                    break;
            }
            MainForm.WriteTextToFile(script);
        }

        void ScriptToClipboard_Click(object sender, EventArgs e)
        {
            
            ToolStripMenuItem tsmi = (ToolStripMenuItem)sender;

            string script = string.Empty;
            switch (tsmi.OwnerItem.Text)
            {
                case "CREATE to":
                    script = this.SelectedSession.GenerateSQLScript();
                    break;
                case "DROP to":
                    script = this.SelectedSession.GenerateDropScript();
                    break;
            }
            MainForm.CopyToClipboard(script);
        }

        void ScriptToSSMS_Click(object sender, EventArgs e)
        {

            ToolStripMenuItem tsmi = (ToolStripMenuItem)sender;

            string script = string.Empty;
            switch (tsmi.OwnerItem.Text)
            {
                case "CREATE to":
                    script = this.SelectedSession.GenerateSQLScript();
                    break;
                case "DROP to":
                    script = this.SelectedSession.GenerateDropScript();
                    break;
            }
            ((MainForm)this.DockPanel.Parent).OpenWithSSMS(script);
        }

        void EditEventSession_Click(object sender, EventArgs e)
        {
            SessionEditorForm editor = new SessionEditorForm(this.SelectedSession, false);
            editor.MdiParent = this.MdiParent;
            editor.Show(this.DockPanel, DockState.Document);
        }

        private EventSession SelectedSession
        {
            get
            {
                if (treeView1.SelectedNode == null)
                    return null;

                TreeNode tn = GetParentNodeByLevel(treeView1.SelectedNode, 1);

                if (tn == null)
                    return null;

                return CurrentServer.EventSessions[tn.Name];

            }
        }

        private EventSessionTarget SelectedTarget
        {
            get
            {
                TreeNode tn = treeView1.SelectedNode;
                TreeNode pn = tn.Parent.Parent;
                return CurrentServer.EventSessions[pn.Text].Targets[tn.Text];
            }
        }

        private SqlServer CurrentServer
        {
            get
            {
                TreeNode tn = GetParentNodeByLevel(treeView1.SelectedNode, 0);

                if (tn == null)
                    return null;

                return servers.ToDictionary(s => s.ServerName, s => s)[tn.Name];
            }
        }


        void DropEventSession_Click(object sender, EventArgs e)
        {

            DialogResult dr = MessageBox.Show("Are you sure you want to drop the " + this.SelectedSession.Name + " session?\r\n\r\nThe current Session State is "+this.SelectedSession.SessionState.ToString()+".", "Drop Session Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (dr == DialogResult.No)
                return;

            if (this.SelectedSession.SessionState == SessionState.Start && MainForm.s.DropActiveWarning)
            {
                dr = MessageBox.Show(this.SelectedSession.Name + " is an Active Session?\r\n\r\nAre you certain you want to drop it?", "Drop Active Session Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (dr == DialogResult.No)
                    return;
            }

            DropEventSession(this.SelectedSession);
        }
        

        void NewEventSession_Click(object sender, EventArgs e)
        {
            EventSession ses = new EventSession(CurrentServer);

            // Override Session Defaults if configured to do so.
            if (MainForm.s.OverrideDefaultOptions)
            {
                ses.EventRetentionMode = s.EventRetentionMode;
                ses.MaxDispatchLatency = s.MaxDispatchLatency;
                ses.MaxEventSize = s.MaxEventSize;
                ses.MaxMemory = s.MaxMemory;
                ses.MemoryPartitionMode = s.MemoryPartitionMode;
                ses.StartupState = s.StartupState;
                ses.TrackCausality = s.TrackCausality;
            }

            SessionEditorForm editor = new SessionEditorForm(ses, true);
            editor.MdiParent = this.MdiParent;
            editor.Show(this.DockPanel, DockState.Document);
        }

        void Refresh_Click(object sender, EventArgs e)
        {
            Load_Form();
        }

        void ViewMetadata_Click(object sender, EventArgs e)
        {
            MetaDataViewerForm m = new MetaDataViewerForm(CurrentServer);
            m.MdiParent = this.MdiParent;
            m.Show(this.DockPanel, DockState.Document);
            m.Text = "[" + CurrentServer.ServerName + "]" + " MetaData";
        }

        #endregion

        public void Load_Form()
        {
            if (servers.Count > 0)
            {
                foreach (SqlServer server in servers)
                {
                    server.Refresh();
                    BindTreeView(server);
                    treeView1.Nodes[0].Expand();
                }
            }
        }

        public void RefreshSelectedItem()
        {
            ReloadNode(GetParentNodeByLevel(treeView1.SelectedNode, 1));
        }

        private TreeNode GetParentNodeByLevel(TreeNode tn, int level)
        {
            if (level == tn.Level)
                return tn;

            if (level > tn.Level || tn.Parent == null)
                return null;

            TreeNode parent = tn.Parent;
            while (parent.Level > level)
            {
                parent = parent.Parent;
            }

            return parent;
        }

        public void RemoveNode(string server, string session)
        {
            TreeNode tn = FindNode(server, session);

            if (tn == null)
                return;

            treeView1.Nodes.Remove(tn);
        }

        private TreeNode FindNode(string server, string session)
        {
            if (treeView1.Nodes.Find(server, false).Count() == 0)
                return null;

            TreeNode tn = treeView1.Nodes.Find(server, false)[0];
            if (tn.Nodes.Find(session, false).Count() == 0)
                return null;

            return tn.Nodes.Find(session, false)[0];
        }

        public void ReloadNode(string server, string session)
        {
            TreeNode tn = FindNode(server, session);

            if (tn == null)
                return;

            ReloadNode(tn);
        }

        private void ReloadNode(TreeNode tn)
        {
            if (tn.Level == 0)
            {
                servers.ToDictionary(s => s.ServerName, s => s)[GetParentNodeByLevel(tn, 0).Name].Refresh();
                //CurrentServer.Refresh();
                BindTreeView(CurrentServer);
            }
            else
            {
                System.Xml.XmlDocument xdoc = servers.ToDictionary(s => s.ServerName, s => s)[GetParentNodeByLevel(tn, 0).Name].EventSessions.Refresh(GetParentNodeByLevel(tn, 1).Name);

                //System.Xml.XmlDocument xdoc = CurrentServer.EventSessions.Refresh(SelectedSession.Name);
               
                XEventTreeNode xtn = (XEventTreeNode)tn;

                xtn.Nodes.Clear();
                xtn.Tag = xdoc.DocumentElement;
                xtn.SelectedImageKey = xtn.getImageKey;
                xtn.ImageKey = xtn.getImageKey;

                
                // SECTION 3. Populate the TreeView with the DOM nodes.
                AddNode(xdoc.DocumentElement, xtn);
                treeView1.Refresh();
            }

        }

        private void EventSession_Load(object sender, EventArgs e)
        {
            Load_Form();
        }

        private void treeView1_BeforeSelect(object sender, TreeViewCancelEventArgs e)
        {
            if (MainForm.PropertiesForm.PropertyGrid.SelectedObject != null)
            {
                if (MainForm.PropertiesForm.PropertyGrid.SelectedObject is EventSession)
                {
                    EventSession es = (EventSession)this.MainForm.PropertiesForm.PropertyGrid.SelectedObject;
                    if (es.IsDirty)
                    {
                        DialogResult dr = MessageBox.Show("There are unsaved changes to the currently selected Session.  Do you want to commit these changes to the Server?", "Uncommited Changes", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);

                    }
                }
            }

            if (this.SelectedSession != null)
            {
                if (this.MainForm.PropertiesForm.PropertyGrid.SelectedObject is EventSession)
                {
                    EventSession es = (EventSession)this.MainForm.PropertiesForm.PropertyGrid.SelectedObject;
                    if (es.IsDirty)
                    { }
                    if (this.SelectedSession.IsDirty)
                    {
                        DialogResult dr = MessageBox.Show("There are unsaved changes to the currently selected Session.  Do you want to commit these changes to the Server?", "Uncommited Changes", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);

                    }
                }
            }
        }

        private MainForm MainForm
        {
            get{return (MainForm)this.DockPanel.Parent;}
        }
    }
}
